

package d20chat;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.Socket;
import java.net.SocketException;

/**
 * Connects to a specified server.
 * Handles all communication on the client end of a session.
 * @author Scott
 */
public class Client
{
    /**
     * Stores an instance of IncomingMessageParser.
     */
    IncomingMessageParser InParse;
    /**
     * Stores an instance of OutgoingMessageParser.
     */
    OutgoingMessageParser OutParse;
    
    /**
     * Stores an instance of d20chatui.
     * Needed to write directly to the chat window if needed.
     */
    private d20chatui tempui;
    
    /**
     * Socket for the current connection.
     */
    private Socket connection;
    
    //the input streams handle the data coming from the server
    /**
     * Stream attached to the socket used for input from the server.
     */
    private InputStream inStream; 
    /**
     * DataInputStream attached to inStream.
     */
    private DataInputStream inDataStream;
    //the output streams handle the data going to the server
    /**
     * Stream attached to the socket used for output to the server.
     */
    private OutputStream outStream;
    /**
     * DataOutputStream attached to outStream.
     */
    private DataOutputStream outDataStream;
    /**
     * Stores an instance of D20Chat.
     * Used to access variables in D20Chat directly.
     */
    private D20Chat theMain;

    
    
    

/**
     * Sets up a connection to a server.
     * Once connection is setup, fires off the listenerloop to keep listening
     * for new messages in a new thread (so the listener doesn't tie up the
     * entire program).
     * @param IP address of server to connect to.
     * @param Port port to connect to on target server.
     */
    public void SetupConnection(String IP, int Port)
    {
        //if connection setup nicely, then fire off listenerloop in new thread
        
        //try required for socket connection stuff
        //try to setup connection and fire off listener loop
        try  
        {
            //build socket based upon passed ip and port info
            connection = new Socket ( IP, Port );
            
            
            //connect streams to the socket
            outStream = connection.getOutputStream ();
            outDataStream = new DataOutputStream ( outStream );
            inStream = connection.getInputStream ();
            inDataStream = new DataInputStream ( inStream );

           theMain.connected = true;
           tempui.showMenuConnectToServer(false);
           if( theMain.isServer == false )
           {
                tempui.showMenuDisconnectToServer(true);
           }
           
           theMain.OutParse.sendMessage( "/systemMessage Connected to server with IP : "+IP+", and Port :"+Port+".\n"  );
            
            //create a mini-class to run listenerloop in
            Thread listenthread = new Thread ( new Runnable() 
            {
                //this run command is now running in it's own thread
                public void run ()
                {
                    //fire off listenerloop in this new thread
                    ListenerLoop();
                }
            });
            //Tell the thread we just created above to actually start
            listenthread.start();

        }  // end try
        catch ( ConnectException except)
        {
            //if error in trying to connect, go here
            //this will NOT catch exceptions from the listenerloop thread
            //ErrorMessageHandler( "Client: Could not find server! "+IP+":"+Port+"\n" );
            theMain.connected = false;
            theMain.OutParse.sendMessage( "/systemMessage Could not find server with IP : "+IP+", and Port :"+Port+".\n"  );
        }
        catch ( IOException except)
        {
            //if error in trying to connect, go here
            //this will NOT catch exceptions from the listenerloop thread
            //ErrorMessageHandler( "Client: Unknown error when trying to connect to server!\n" );
            theMain.connected = false;
            theMain.OutParse.sendMessage( "/systemMessage Unknown error when trying to connect to server with IP : "+IP+", and Port :"+Port+".\n"  );
            except.printStackTrace ();
        }  // end catch
    }
    
    

/**
     * Closes connection, and thus kills listenerloop thread.
     */
    public void CloseConnection()
    {
        
        try 
        {
            //closes connection
            //when connection closes, it will cause listenerloop to try
            //and read from a dead stream, which will throw a handled exception
            //and close down the thread
            connection.close ();
            
            theMain.connected = false;
            tempui.showMenuConnectToServer(true);
            tempui.showMenuDisconnectToServer(false);
            
            //output to chat informing of connection closed
            //ErrorMessageHandler ( "Client: Connection closed!\n" );
            //theMain.OutParse.sendMessage( "/systemMessage Connection closed!\n"  );

        }  
        catch ( IOException except )  
        {
            //if closing fails for some reason or another, output to chat window
            //ErrorMessageHandler ( "Client: Error closing connection\n" );
            theMain.OutParse.sendMessage( "/systemMessage Error closing connection\n"  );
            except.printStackTrace ();
        }
    }
    
    

/**
     * listens to the connection for new messages. 
     * When they are recieved it will spin off a new thread and give the message 
     * to checkType
     */
    private void ListenerLoop()
    {
        //todo: Need to add code to handle thread interupts and check flags
        
        String message; //temporary storage to store incoming message
        
        //try required for socket streaming
        //try reading from the socket
        //if the socket ceases to exist (closes) the streams will not behave
        //and will throw an exception which will allow the thread to proceed to
        //the catch and then exit the thread
        try 
        { 
            //keep looping forever (until an exception throws, likely from the socket closing
            while(true)
            {
                
                //read in the message from the stream
                message = inDataStream.readUTF();
                
                //should we be checking to see if the message is empty before
                //passing the message to something? Is it going to keep passing over and over
                //or does the readUTF block until it gets a message?
                
                //post the message to the chat window
                //will likely want to call a real threadsafe function in the future
                
                InParse.CheckType(message);
            }
        } 
        //handles all exceptions. Likely it will only be exceptions due to the socket
        //being closed, but may need to handle interupts in the future (and then check flags below)
        catch (SocketException ex)
        {
            theMain.connected = false;
            tempui.showMenuConnectToServer(true);
            tempui.showMenuDisconnectToServer(false);
            theMain.OutParse.sendMessage("/systemMessage You are no longer connected to the server.");
            //ErrorMessageHandler ( "Client: ListenerLoop shutting down\n" );
            //Socket is shutdown, so we can't read from it. Client shutting down now.
        }
        catch (IOException ex) 
        {
            theMain.connected = false;
            tempui.showMenuConnectToServer(true);
            tempui.showMenuDisconnectToServer(false);
            theMain.OutParse.sendMessage("/systemMessage You are no longer connected to the server.");
            //ErrorMessageHandler ( "Client: Unknown error in ListenerLoop!\n" );
            ex.printStackTrace();
        }
    }
    
    

/**
     * Sends a message to the server that client is connected to.
     * Call this function from anywhere in the program to send a message to the server.
     * Primarily called by OutGoingMessageParser.
     * @param message Message to be sent to server that client is connected to.
     */
    public void SendMessage(String message)
    {
        //try required for socket streaming
        try 
        {
            //send the message to the server
            outDataStream.writeUTF ( message );
        } 
        //if the message can not send, let the user know
        catch (SocketException ex)
        {
            //ErrorMessageHandler ( "Client: Not connected to a server!\n" );
            theMain.OutParse.sendMessage( "/systemMessage Not connected to a server!\n"  );
            
        }
        catch (IOException ex) 
        {
            //ErrorMessageHandler ( "Client: Unknown error while sending message!\n" );
            theMain.OutParse.sendMessage( "/systemMessage Unknown error while sending message!\n"  );
            ex.printStackTrace();
            //might be good to call a different function to let user know of error
            //or perhaps just return a number to let the caller know it caused an error
            
        }
     }

    

/**
     * Class Constructor for Client.
     * @param gui current instance of d20chatui.
     * @param tempMain current instance of D20Chat.
     */
    public Client(d20chatui gui, D20Chat tempMain) 
    {
        //set the variable to know where the gui is
        //in the future this may not be needed if we can use some other sort of function calls
        tempui = gui; 
        theMain = tempMain;
    }
   
    
/********************************************************************************
  Method:
  Description:
  Parameters:
  Used by:
  Uses:
********************************************************************************/
    private void ErrorMessageHandler(String message)
    {
        tempui.tempchatwindowoutput( message );
        
    }
}
